from typing import List

import torch
import numpy as np
import pandas as pd
import math

# .txt 和 .csv 其实都是可以被读取的，具体的读取方式得根据业务需求发生改变，但是大方向基本上是不变的
# 大前提：根据 .csv 数据 获取 课程信息

# 获取常规的课表，这个方案理论上可以读取一个班级课表
# schedule_without_experiment = pd.read_csv('../data/schedule_without_experiment.csv', delimiter=',', header=0)

# 组会讨论：针对一个老师教授多个班级有几个方案
# 方案一：把几个 .csv 文件进行合并，形成一个总的表，然后再进行处理
# 方案二：让我们的模型轮着读 .csv 文件，在模型端组合成一个大课表（张量？）
# 老师的课表和学生的课表有什么区别呢？老师可以知道自己的所有教学班合并成的大课表吗？

# 获取老师的志愿，我们先暂时用 .csv 进行表示，事实上是可以从前后端交互的数据库中获取的
priority_of_schedule = pd.read_csv('../data/priority_of_schedule.csv', delimiter=',', header=0)

# 获取网络训练的原始数据
data = pd.read_csv('../data/data.csv', delimiter=',', header=0)

# 既然前后端的同学可以保证老师的所有志愿都不冲突，算法岗位的同学就默认老师给的方案“可行”了

# 利用 torch 的张量构造一个课表张量
# 设置张量的形状
weeks = 16
days_per_week = 7
classes_per_day = 12

# 初始化一个全零的张量（ 一开始没有课程 ）
schedule_tensor = torch.zeros(weeks, days_per_week, classes_per_day)

# 如果需要填充具体的课程信息，可以像这样操作：
# schedule_tensor[week_index, day_index, class_index] = class_info
# 其中 class_info 是一个表示课程信息的张量或者标量

# 实验课也分为三种
experiment_course_tensors = [
    torch.tensor([0, 32]),  # 大课
    torch.tensor([1, 16]),  # 中课
    torch.tensor([2, 8])  # 小课
]

# 每一周排课的方法总共有三种
course_schedule_tensors = [
    torch.tensor([0, 2]),  # 短课
    torch.tensor([1, 4]),  # 中课
    torch.tensor([2, 8])  # 长课
]

# 组会要讨论的问题：用位图来确定软件真的好吗？如果真的好，究竟应该如何设计合理的位图来应对复杂多变的情况？

# 算法岗近期的工作就是 设计公式 来评判这一门实验课到底好不好
# 我们先按照算法思路走一遍代码

def reinforcement_learning_model():
    # 求参数
    pass

def standard_deviation_model(standard_deviation: int):
    return 0

def judge_schedule(schedule_tensor, priority_of_schedule) -> int:
    # 获取硬编码参数 hsv，前后端的同学已经规避了 hsv，所以这个参数可以考虑一票否决
    result = []
    # 求对应的比重
    scv = scv_model(schedule_tensor, priority_of_schedule)
    weekend = weekend_model(schedule_tensor, priority_of_schedule)
    efficient = efficient_model(schedule_tensor, priority_of_schedule)
    priority = priority_model(schedule_tensor, priority_of_schedule)
    for i in range(len(priority_of_schedule)):
        # 参数比重相乘取最大值并返回结果
        result[i] = (scv[i] + weekend[i] + efficient[i] + priority[i])
    return max(result)


# 计算软编码参数 scv，scv 可以用很多方法进行复杂地设计
# 备注，为了解析代码方便，以下的打分仅仅是为了说明算法思路，实际的打分还需要考虑很多因素！
def scv_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 60

        if row['Course_Sequence'][-1] == 12:
            point -= 5  # 如果第 12 节课要上课，扣分

        if row['Course_Sequence'][0] % 2 == 1:
            point += 5  # 优先考虑 1 3 5 7 9 开头的课

        day = row['Day']

        pass  # 把这一天的安排加入到原来的课表中，计算课程密度，课程密度越高越好

        total_courses_per_day = 0  # 假如说我们已经求了课程密度
        course_dense = total_courses_per_day / 12

        point += course_dense * 5  # 当天课程密度越高，分数越高，具体的形式是表格赋分还是动态打分有待商榷

        count_courses_per_week = 0  # 假如说我们已经求了一周内的课程次数
        if count_courses_per_week > 1:
            point -= count_courses_per_week * 5

        # 课程的排列是平均分配的，不考虑周末的情况
        # 假如说我们可以获取一周之内的课程的分布情况，那么可以计算出课程在工作日平均分布情况
        list_of_courses_per_week = [5, 5, 5, 5, 5, 0, 0]
        # 求标准差，然后进行标准差赋值
        # 计算平均值
        average = sum(list_of_courses_per_week) / 5
        # 计算每个数值与平均值的差的平方
        squared_diffs = [(x - average) ** 2 for x in list_of_courses_per_week]
        # 计算方差
        variance = sum(squared_diffs) / 4  # 使用 4 作为除数进行样本方差的计算
        # 计算标准差
        standard_deviation = math.sqrt(variance)
        point += standard_deviation_model(standard_deviation)  # 标准差的分数，这个函数需要根据实际情况进行设计

        # 周数越连续分数越高
        # TODO：如果不连续，奇偶周也可以进行加分
        df_sorted = row.sort_values('Week', ascending=True)
        sorted_score = 0
        for i in range(1, len(df_sorted)):
            if df_sorted.iloc[i]['Week'] - df_sorted.iloc[i - 1]['Week'] == 1:
                sorted_score += 1

        if sorted_score > 0:
            point += sorted_score * 5

        # 志愿优先级越高，越容易被选中
        point += (5 - row['Application_Priority'])
        print(point)
        result.append(point)

    return result


def weekend_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 10
        day = row['Day']
        if day == 6 or day == 7:
            point -= 5
        result.append(point)

    return result

def efficient_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 15
        efficiency = row['Number_Of_Students'] / row['Volume']
        if efficiency > 0.75:
            result.append(point)
        else:
            result.append(point * efficiency)
    return result

def priority_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 5
        priority = row['Application_Priority']
        if priority == 1:
            point += 5
        elif priority == 2:
            point += 3
        elif priority == 3:
            point += 1
        result.append(point)
    return result

# 考虑数据结构还有老师的评价，考虑和后端同学沟通
# 从而实现一个不断完善的过程！
# 要留一个反馈优化的空间